Skip to content

Add search UI for full-text entry search#809

Open
brendanlong wants to merge 1 commit intomasterfrom
claude/1075f1c0-5eb4-42a2-95a5-97c08385b793
Open

Add search UI for full-text entry search#809
brendanlong wants to merge 1 commit intomasterfrom
claude/1075f1c0-5eb4-42a2-95a5-97c08385b793

Conversation

@brendanlong
Copy link
Copy Markdown
Owner

Summary

  • Adds a /search page with a debounced search input that leverages the existing backend full-text search (entries.list query parameter with PostgreSQL ts_rank relevance ranking)
  • Adds a search icon button in the header and / keyboard shortcut for quick access (like Gmail/GitHub)
  • When no query is entered, shows a "Type a search query" prompt; when query has no matches, shows "No matching entries found"

Closes #565

Details

The backend already supported full-text search via the entries.list endpoint's query parameter (using PostgreSQL to_tsvector/plainto_tsquery), but it was only exposed through the MCP server. This PR adds the frontend UI.

Architecture

  • query parameter added to EntriesListInput and EntriesListFilters so it flows through the existing cache key infrastructure
  • useEntriesListInput reads the ?q= URL parameter on the /search page
  • SearchInput component uses a controlled input with 300ms debounce, updating the URL via clientReplace
  • Search page defaults to unreadOnly: false and hides sort toggle (results ranked by relevance)
  • Keyboard shortcuts are disabled while the search input is focused

Files changed

  • entries-list-input.ts - Added query to input/filter interfaces
  • useEntriesListInput.ts - Reads ?q= from URL search params
  • EntryListPage.tsx - Passes q param for server-side prefetching
  • UnifiedEntriesContent.tsx - Added /search route with SearchInput title slot
  • AppLayoutContent.tsx - Search icon in header
  • useKeyboardShortcuts.ts - / shortcut navigates to search
  • KeyboardShortcutsModal.tsx - Documents / shortcut
  • New: search/page.tsx, SearchInput.tsx, SearchIcon

Test plan

  • Navigate to /search via header icon — search input autofocuses
  • Type a query — results appear after 300ms debounce, ranked by relevance
  • Clear search input — shows "Type a search query" prompt
  • Press / key from any entry list — navigates to search page
  • Browser back/forward works correctly with search queries
  • Toggle unread filter on search page — preserves search query in URL
  • Press ? — keyboard shortcuts modal shows / for search
  • Keyboard shortcuts (j/k/etc.) don't fire while typing in search input

🤖 Generated with Claude Code

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements a new full-text search feature, including a dedicated /search route, a debounced SearchInput component, and necessary updates to the entry list fetching logic to support query parameters. The review identified issues regarding redundant keyboard shortcut management, side effects during the render phase in the SearchInput component, and the need for immediate URL updates when clearing the search input.

const urlQuery = searchParams?.get("q") ?? "";
const [inputValue, setInputValue] = useState(urlQuery);
const inputRef = useRef<HTMLInputElement>(null);
const { setEnabled: setKeyboardShortcutsEnabled } = useKeyboardShortcutsContext();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The manual toggling of keyboard shortcuts is redundant. The useKeyboardShortcuts hook in this project already uses enableOnFormTags: false for all its hotkeys, which automatically disables them when an input or search field is focused. Removing this manual state management simplifies the component.

Suggested change
const { setEnabled: setKeyboardShortcutsEnabled } = useKeyboardShortcutsContext();
const inputRef = useRef<HTMLInputElement>(null);

Comment on lines +33 to +38
if (urlQuery !== lastWrittenRef.current) {
lastWrittenRef.current = urlQuery;
if (urlQuery !== inputValue) {
setInputValue(urlQuery);
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Mutating a ref during the render phase is a side effect that violates React's rules. This can lead to unpredictable behavior in concurrent rendering. Instead, use the recommended pattern for adjusting state when a prop (or URL parameter) changes by tracking the previous value in state.

Suggested change
if (urlQuery !== lastWrittenRef.current) {
lastWrittenRef.current = urlQuery;
if (urlQuery !== inputValue) {
setInputValue(urlQuery);
}
}
const [prevUrlQuery, setPrevUrlQuery] = useState(urlQuery);
if (urlQuery !== prevUrlQuery) {
setPrevUrlQuery(urlQuery);
setInputValue(urlQuery);
}

Comment on lines +74 to +77
const handleClear = useCallback(() => {
setInputValue("");
inputRef.current?.focus();
}, []);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The clear action should update the URL immediately to provide instant feedback to the user. Currently, it relies on the 300ms debounce timer, which causes the previous search results to linger after the input is cleared.

  const handleClear = useCallback(() => {
    setInputValue("");
    const params = new URLSearchParams(searchParams?.toString() ?? "");
    params.delete("q");
    const queryString = params.toString();
    const url = queryString ? "/search?" + queryString : "/search";
    clientReplace(url);
    inputRef.current?.focus();
  }, [searchParams]);

@brendanlong brendanlong force-pushed the claude/1075f1c0-5eb4-42a2-95a5-97c08385b793 branch from b470d95 to bad2374 Compare April 8, 2026 20:38
Adds a /search page that exposes the existing backend full-text search
(entries.list query parameter) in the UI. The search input debounces
user input and updates the URL's ?q= parameter, which drives the
PostgreSQL full-text search with relevance ranking.

Features:
- Search page at /search with autofocusing search input
- Search icon button in the header for quick access
- "/" keyboard shortcut navigates to search (like Gmail/GitHub)
- Debounced input (300ms) updates URL, triggering new search
- Results ranked by relevance via PostgreSQL ts_rank
- "Type a search query" prompt when no query entered
- Clear button to reset search
- Keyboard shortcuts disabled while search input is focused
- Search documented in keyboard shortcuts modal (? key)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@brendanlong brendanlong force-pushed the claude/1075f1c0-5eb4-42a2-95a5-97c08385b793 branch from bad2374 to d8d6b5d Compare April 8, 2026 20:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support search in the UI

2 participants